package com.flow.framework.common.json;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.flow.framework.common.constant.FrameworkCommonConstant;
import com.flow.framework.common.error.SystemErrorCode;
import com.flow.framework.common.exception.CheckedException;
import com.flow.framework.common.type.TypeReference;

import java.util.*;

/**
 * jsonArray适配
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/1/2
 */
public class JsonArray extends ArrayList {

    private static final long serialVersionUID = 5871970967397101346L;

    private List<Object> elements;

    private JsonArray() {
        this.elements = new ArrayList<>();
    }

    private static ObjectMapper getObjectMapper() {
        return ObjectMapperHolder.getInstance();
    }

    public static JsonArray fromObject(Object object) {
        if (object instanceof JsonArray) {
            return (JsonArray) object;
        }
        JsonArray jsonArray = new JsonArray();
        try {
            if (object instanceof String) {
                List<Object> list = getObjectMapper().readValue(object.toString(), new TypeReference<List<Object>>() {
                });
                convertElement(jsonArray, list);
            } else if (object instanceof Collection || object instanceof String[]) {
                String arrayStr = getObjectMapper().writeValueAsString(object);
                return fromObject(arrayStr);
            }
            return jsonArray;
        } catch (Exception e) {
            throw new CheckedException(SystemErrorCode.SERIALIZE_ERROR, "object to json array error.", e);
        }
    }


    /**
     * 如果传递的参数为string，则直接返回传入的字符串，如果为null，则返回null
     *
     * @param obj obj
     * @return
     */
    public static String toString(Object obj) {
        try {
            return getObjectMapper().writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new CheckedException(SystemErrorCode.SERIALIZE_ERROR, "object to json string is empty.", e);
        }
    }

    private static void convertElement(JsonArray array, List<Object> list) {
        Object tempElement = null;

        for (Iterator iterator = list.iterator(); iterator.hasNext(); array.elements.add(tempElement)) {
            Object element = iterator.next();
            tempElement = element;
            if (element instanceof Map && !(element instanceof JsonObject)) {
                tempElement = JsonObject.fromObject(element);
            } else if (element instanceof Collection && !(element instanceof JsonArray)) {
                tempElement = fromObject(element);
            }
        }
    }

    @Override
    public String toString() {
        try {
            return getObjectMapper().writeValueAsString(this.elements);
        } catch (JsonProcessingException e) {
            throw new CheckedException(SystemErrorCode.SERIALIZE_ERROR, "json array to string error.", e);
        }
    }

    public static <T> List<T> toCollection(JsonArray array, Class<T> objectClass) {
        if (null == array) {
            throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "json array is null.");
        }
        try {
            ObjectMapper objectMapper = getObjectMapper();
            return objectMapper.readValue(array.toString(),
                    objectMapper.getTypeFactory().constructCollectionType(List.class, objectClass));
        } catch (Exception e) {
            throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "json array to object error.", e);
        }
    }

    public static <T> T toCollection(JsonArray array, TypeReference<T> typeReference) {
        if (null == array) {
            throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "json array is null.");
        }
        try {
            return getObjectMapper().readValue(array.toString(), typeReference);
        } catch (Exception e) {
            throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "json array to object error.", e);
        }
    }

    public static <T> List<T> toCollection(String array, Class<T> objectClass) {
        if (null == array) {
            throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "json array is null.");
        }
        try {
            ObjectMapper objectMapper = getObjectMapper();
            return objectMapper.readValue(array,
                    objectMapper.getTypeFactory().constructCollectionType(List.class, objectClass));
        } catch (Exception e) {
            throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "json array to object error.", e);
        }
    }

    public static <T> T toCollection(String array, TypeReference<T> typeReference) {
        if (null == array) {
            throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "json array is null.");
        }
        try {
            return getObjectMapper().readValue(array, typeReference);
        } catch (Exception e) {
            throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "json array to object error.", e);
        }
    }

    private void checkIndex(int index) {
        if (index < 0 || index >= this.elements.size()) {
            throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "index is out of size. index : " + index);
        }
    }

    public JsonObject getJsonObject(int index) {
        checkIndex(index);
        Object value = this.elements.get(index);
        if (value instanceof JsonObject) {
            return (JsonObject) value;
        }
        throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "element isn't json object.");
    }

    public String getString(int index) {
        checkIndex(index);
        Object value = this.elements.get(index);
        return null == value ? null : value.toString();
    }

    public Boolean getBoolean(int index) {
        checkIndex(index);
        Object value = this.elements.get(index);
        if (null != value) {
            if (JsonObject.isFalse(value)) {
                return false;
            }
            if (JsonObject.isTrue(value)) {
                return true;
            }
        } else {
            return null;
        }
        throw new CheckedException(SystemErrorCode.DESERIALIZATION_ERROR, "the index element is null.");
    }

    @Override
    public boolean add(Object object) {
        if (null == object) {
            throw new CheckedException(SystemErrorCode.PARAMS_ERROR, "can't add null value to array.");
        }
        Object tempValue = this.convertValue(object);
        return this.elements.add(tempValue);
    }

    private Object convertValue(Object object) {
        Object tempValue = object;
        if (object instanceof String) {
            tempValue = this.convertStr2Json(object);
        } else if (object instanceof Map && !(object instanceof JsonObject)) {
            tempValue = JsonObject.fromObject(object);
        } else if (object instanceof Collection && !(object instanceof JsonArray)) {
            tempValue = fromObject(object);
        }
        return tempValue;
    }

    private Object convertStr2Json(Object value) {
        Object tempValue = value;
        try {
            if (((String) value).startsWith(FrameworkCommonConstant.LEFT_BRACE)
                    && ((String) value).endsWith(FrameworkCommonConstant.RIGHT_BRACE)) {
                tempValue = JsonObject.fromObject(value);
            } else if (((String) value).startsWith(FrameworkCommonConstant.LEFT_BRACKET)
                    && ((String) value).endsWith(FrameworkCommonConstant.RIGHT_BRACKET)) {
                tempValue = fromObject(value);
            }
        } catch (Exception e) {
            throw new CheckedException(SystemErrorCode.SERIALIZE_ERROR, "convert string to json or json array error.", e);
        }
        return tempValue;
    }

    @Override
    public void add(int index, Object object) {
        if (index < 0) {
            throw new CheckedException(SystemErrorCode.PARAMS_ERROR, "index out of limit. index : " + index);
        }
        if (null == object) {
            throw new CheckedException(SystemErrorCode.PARAMS_ERROR, "can't add null value to array.");
        }
        Object tempValue = this.convertValue(object);
        this.elements.add(index, tempValue);
    }

    @Override
    public boolean addAll(Collection objects) {
        if (null != objects && !objects.isEmpty()) {
            for (Object obj : objects) {
                this.add(obj);
            }
            return true;
        }
        return false;
    }

    @Override
    public boolean addAll(int index, Collection objects) {
        if (index < 0) {
            throw new CheckedException(SystemErrorCode.PARAMS_ERROR, "index out of limit. index : " + index);
        }
        int tempIndex = index;
        if (null != objects && !objects.isEmpty()) {
            for (Object obj : objects) {
                this.add(tempIndex, obj);
                tempIndex++;
            }
            return true;
        }
        return false;
    }

    @Override
    public void clear() {
        this.elements.clear();
    }

    @Override
    public boolean contains(Object object) {
        return this.elements.contains(object);
    }

    @Override
    public boolean containsAll(Collection objects) {
        return this.elements.containsAll(objects);
    }

    @Override
    public Object get(int index) {
        checkIndex(index);
        return this.elements.get(index);
    }

    @Override
    public int indexOf(Object object) {
        return this.elements.indexOf(object);
    }

    @Override
    public boolean isEmpty() {
        return this.elements.isEmpty();
    }

    @Override
    public Iterator iterator() {
        return this.elements.iterator();
    }

    @Override
    public int lastIndexOf(Object object) {
        return this.elements.lastIndexOf(object);
    }

    @Override
    public ListIterator listIterator() {
        return this.elements.listIterator();
    }

    @Override
    public ListIterator listIterator(int index) {
        return this.elements.listIterator(index);
    }

    @Override
    public boolean remove(Object object) {
        return this.elements.remove(object);
    }

    @Override
    public Object remove(int index) {
        checkIndex(index);
        return this.elements.remove(index);
    }

    @Override
    public boolean removeAll(Collection objects) {
        return this.elements.removeAll(objects);
    }

    @Override
    public boolean retainAll(Collection objects) {
        return this.elements.retainAll(objects);
    }

    @Override
    public Object set(int index, Object object) {
        checkIndex(index);
        return this.elements.set(index, object);
    }

    @Override
    public int size() {
        return this.elements.size();
    }

    @Override
    public List subList(int begin, int end) {
        checkIndex(begin);
        checkIndex(end);
        return this.elements.subList(begin, end);
    }

    @Override
    public Object[] toArray() {
        return this.elements.toArray();
    }

    @Override
    public Object[] toArray(Object[] objects) {
        return this.elements.toArray(objects);
    }
}
